package com.wangwq.myspringredis.aspect;

import com.wangwq.myspringredis.annotation.RedisCacheEvict;
import com.wangwq.myspringredis.annotation.RedisCacheGet;
import com.wangwq.myspringredis.annotation.RedisCachePut;
import com.wangwq.myspringredis.util.RedisUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;


@Aspect
@Component
public class RedisCacheAspect {

    private final static char DOT = '.';
    private final static char KEY_PTRFIX = '#';

    @Autowired
    private  RedisUtil redisUtil;

    /**
     * 设置缓存
     * @param proceedingJoinPoint
     * @param redisCachePut
     * @return
     * @throws Throwable
     */
    @Around("@annotation(redisCachePut)")
    public  Object cachePut(ProceedingJoinPoint proceedingJoinPoint, RedisCachePut redisCachePut) throws Throwable {
        Object keyObject = getCacheKey(proceedingJoinPoint,redisCachePut.key());
        Object result = proceedingJoinPoint.proceed();

        if(result != null && keyObject != null){
//            redisUtil.set(redisCachePut.cacheName(),keyObject,result,redisCachePut.expire());
            redisUtil.set(keyObject.toString(),result.toString());
        }
        return result;
    }

    /**
     * 获取缓存
     * @param proceedingJoinPoint
     * @param redisCacheGet
     * @return
     * @throws Throwable
     */
    @Around("@annotation(redisCacheGet)")
    public  Object cacheGet(ProceedingJoinPoint proceedingJoinPoint, RedisCacheGet redisCacheGet) throws Throwable {
        Object keyObject = getCacheKey(proceedingJoinPoint,redisCacheGet.key());
        Object result = null;
        if(keyObject != null && !StringUtils.isEmpty(keyObject)){
            result=   redisUtil.get(keyObject.toString());
        }
        //缓存没有命中，或不存在，就去获取数据库，再存入缓存中
        if(result == null){
            result = proceedingJoinPoint.proceed();
        }
        Class clazz = getAdvicedMeth(proceedingJoinPoint).getReturnType();
        if(clazz != Void.TYPE){
            redisUtil.set(keyObject.toString(),result.toString());
        }
        return result;

    }

    /**
     * 删除缓存
     * @param proceedingJoinPoint
     * @param redisCacheEvict
     * @return
     * @throws Throwable
     */
    @Around("@annotation(redisCacheEvict)")
    public Object cacheEvict(ProceedingJoinPoint proceedingJoinPoint, RedisCacheEvict redisCacheEvict) throws Throwable {
        Object keyObject = getCacheKey(proceedingJoinPoint,redisCacheEvict.key());
        if(keyObject != null && !StringUtils.isEmpty(keyObject)){
            redisUtil.remove(keyObject.toString());
        }
        return proceedingJoinPoint.proceed();

    }




    /**
     * 获取缓存的key对象
     * @param proceedingJoinPoint
     * @param key
     * @return
     * @throws NoSuchMethodException
     */
     private Object getCacheKey(ProceedingJoinPoint proceedingJoinPoint, String key) throws Exception {
         //以#开头
         if(key.length() > 0 && key.charAt(0) == KEY_PTRFIX){

              key = key.substring(1);
             // 将key分割成属性和参数名，第一个“.”之前是参数名，之后是属性名称
              int dotIndex = key.indexOf(DOT);
              String argName = key;
              if( dotIndex  > 0){
                   argName = key.substring(0,dotIndex);
                   key = key.substring(dotIndex+1);
              }
              //获取参数值
              Object argVal = getArg(proceedingJoinPoint,argName);
               //获取参数的属性值
             Object objectKey = argVal;
             if(dotIndex > 0 ){
                 objectKey = getObjectKey(argVal,key);
             }
             return objectKey;
         }else{
             return key;
         }
     }

    /**
     * 获取参数
     * @param proceedingJoinPoint
     * @param parameterName
     * @return
     * @throws NoSuchMethodException
     */
     private  Object getArg(ProceedingJoinPoint proceedingJoinPoint,String parameterName) throws  NoSuchMethodException{

         Method method = getAdvicedMeth(proceedingJoinPoint);
         ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
         String[] parameterNames = parameterNameDiscoverer.getParameterNames(method);
         if(parameterNames != null){
             int index = 0;
             for (String name : parameterNames){
                 if(name.equals(parameterName)){
                     return proceedingJoinPoint.getArgs()[index];
                 }
                 index++;
             }
         }
         throw new IllegalArgumentException("no such parameter name: [" + parameterName + "] in method: " + method);

     }
    /**
     * 获取拦截的方法
     *
     * @param连接点
     * @return
     * @throws NoSuchMethodException
     */
     private Method getAdvicedMeth(ProceedingJoinPoint  proceedingJoinPoint ) throws NoSuchMethodException {
         Signature signature =  proceedingJoinPoint.getSignature();
         MethodSignature methodSignature = null;
         if(!(signature instanceof  MethodSignature)){
             throw new IllegalArgumentException("annotation can only use to method.");
         }
         methodSignature = (MethodSignature) signature;
         Object target = proceedingJoinPoint.getTarget();
         return target.getClass().getMethod(methodSignature.getName(),methodSignature.getParameterTypes());
     }

    /**
     *   * 获取从object上获取key所对应的属性对象
     * 例如：key: country.province.city.town
     * 就相当于调用：object.getCountry().getProvince().getCity.getTown()
     * @param object
     * @param key
     * @return
     * @throws Exception
     */
        private Object getObjectKey(Object object, String key) throws Exception {
            if(StringUtils.isEmpty(key)){
                return object;
            }

            int index = key.indexOf(DOT);
            if(index > 0){
                 String propertyName = key.substring(0,index);
                 key = key.substring(index+1);
                 Object property = getProperty(object,getterMethod(propertyName));
                return getObjectKey(property,key);
            }else{
                return getProperty(object,getterMethod(key));
            }
        }

    /**
     * 获取name的getter方法名称
     *
     * @param name
     * @return
     */
    private String getterMethod(String name) {
        return "get" + Character.toUpperCase(name.charAt(0)) + name.substring(1);
    }

    /**
     * 调用obj对象上的getterMethodName
     *
     * @param obj
     * @param getterMethodName
     * @return
     * @throws Exception
     */
    private Object getProperty(Object obj, String getterMethodName) throws Exception {
        return obj.getClass().getMethod(getterMethodName).invoke(obj);
    }

}
